#!/usr/bin/env python
#-*- coding:utf-8 -*-

import threading
import socket
from multiprocessing import Manager
import time
import hashlib
import base64
import struct
import json
import hashlib

'''

class ReportWebsocket(object):

    # 队列属性 存储webs接受到的c
    # 服务器阵亡表
    web_servers =[]
    def __init__(self):
        self.queue = Manager().Queue()
        self.info_queue = Manager().Queue()
        self.client_map = {}
        self.current_day = time.gmtime().tm_yday
        self.server_catalog = {'news':self.server_news}
        self.updata_read_log()

    def server_news(self,name,data=None):
        if data['last_time'] == self.log_source.last_time:
            return
        while self.log_source.status == 1:
            time.sleep(1)
        data = self.log_source.get_report_obj()
        send_data = {
            'name':'news',
            'data':data
        }
        self.from_name_sendMessage(name,json.dumps(send_data,ensure_ascii=False))
        self.client_map[name]['require']['news']['last_time']= self.log_source.last_time
        pass

    def from_name_sendMessage(self, name, message):
        self.client_map[name]['thread'].sendMessage(message)

    def updata_read_log(self):
        self.log_source = ReadCrawlLog()
        self.log_source.start()

    def start_web_server(self):
        web_s = WebSocketServer(9000,self,len(self.web_servers) )
        self.web_servers.append({'server':web_s, 'status':1})
        web_s.start()
        return web_s

    def start_web_client(self):
        tmp_data = self.queue.get()
        c =WebsocketClient(tmp_data['socket'],tmp_data['name'], self)
        client_o = {
            'socket': tmp_data['socket'],
            'thread':c,
            'require':{}
        }
        self.client_map[tmp_data['name']] = client_o
        c.start()
        print(tmp_data)
        pass

    def work(self):
        while True:
            if len(self.web_servers) <=0 or self.web_servers: # 或者死去
                self.__web_server = self.start_web_server()
            if not self.queue.empty():
                # t = threading.Thread(target=self.start_web_client, args=(None,))
                t = threading.Thread(target=self.start_web_client)
                t.start()
            elif not self.info_queue.empty():
                self.handle_str()
                pass
            else:
                self.handle_server()
                #检查服务
            time.sleep(1)
            # self.queue.put({"1":1,"A":2})

            # self.start_web_client()
        #启动webserver
        pass

    def handle_str(self):
        task = self.info_queue.get()
        index_str = task['data']
        if index_str == '订阅最新数据':
            self.client_map[task['name']]['require']['news'] = {'status':1,'last_time':0}
            self.client_map[task['name']]['thread'].sendMessage(json.dumps({'name':'common','data':'订阅成功'},ensure_ascii=False))
        elif index_str == '你好':
            self.client_map[task['name']]['thread'].sendMessage(json.dumps({'name':'common','data':'你好'},ensure_ascii=False))
        elif index_str =='新数据':
            self.from_name_sendMessage(task['name'],json.dumps({'name':'common','data':self.log_source.get_report_obj()},ensure_ascii=False))
        elif index_str =='退订最新数据':
            del(self.client_map[task['name']]['require']['news'])
            self.client_map[task['name']]['thread'].sendMessage(json.dumps({'name': 'common', 'data': '退订成功'}, ensure_ascii=False))
        elif index_str == '全局数据':
            self.client_map[task['name']]['thread'].sendMessage(json.dumps({'name': 'common', 'data': {
                "启动时间":list(self.log_source.brief_info['start_time']),
                "日工作量":self.log_source.brief_info['day_achieve'],
                "日错误集":list(self.log_source.brief_info['day_error']),
            }}, ensure_ascii=False))
        elif index_str == '断开连接':
            self.client_map[task['name']]['thread'].stop_threading()
            del(self.client_map[task['name']])
        elif index_str == '待处理任务':
            try:
                tmp_db = APNAboutInfoDB(ip='rm-wz901r6j98pdfx880o.mysql.rds.aliyuncs.com', port='3306', user='hjgrds',
                               pwd='!9fbd0d13a3', db='zhiquan')
                fn = lambda x: True if len(x) == 9 or len(x) == 13 else False
                tmp_new_db_data=tmp_db.get_new_apn()
                tmp_updata_db_data = tmp_db.get_need_apn()
                self.from_name_sendMessage(task['name'],json.dumps({'name': 'common', 'data': {
                    '未爬过的任务(合格/不合格)':[len(list(filter(fn, tmp_new_db_data))),len(tmp_new_db_data)],
                    '待更新的任务(合格/不合格)':[len(list(filter(fn, tmp_updata_db_data))),len(tmp_updata_db_data)]
                }}, ensure_ascii=False))
            finally:
                tmp_db.close_db()
        elif index_str == 'help':
            self.from_name_sendMessage(task['name'],json.dumps({'name': 'common', 'data':'***帮助指令***\n你好\n订阅最新数据\n新数据\n退订最新数据\n全局数据\n断开连接\n待处理任务'},ensure_ascii=False))

        pass

    def handle_server(self):
        for key in self.client_map.keys():
            tmp_client = self.client_map[key]
            for key2 in tmp_client['require']:
                self.server_catalog[key2](key,tmp_client['require'][key2])
                pass
        pass
        
'''


class WebSocketManage(object):

    web_servers = []
    def __init__(self):
        self.queue = Manager().Queue()
        self.client_map = {}  #webscoket map{name, client_o}
        # client_o = {
        #     'socket': tmp_data['socket'],
        #     'thread': c,
        #     'require': {}
        # }
        self.nickname_client = {}
        self.info_queue = Manager().Queue()
        self.room_map = {}
        self.room_map['世界'] = {'name': '世界', 'password': '', 'member': set()}
        pass

    def send_message_all(self, info):
        for c_index in self.client_map:
            self.client_map[c_index]['thread'].sendMessage(info)
            pass
        pass

    def get_md5(self, byter_info):
        m = hashlib.md5()
        m.update(byter_info)
        return m.hexdigest()

    def send_message_to_room(self, room, nickname, message):
        message = {'room': room, 'nickname': nickname,
                   'message': message, 'class': 'info'}
        for username in self.room_map[room]['member']:
            self.client_map[username]['thread'].sendMessage(json.dumps(message, ensure_ascii=False))

    def start_web_server(self):
        web_s = WebSocketServer(9000, self, len(self.web_servers))
        self.web_servers.append({'server': web_s, 'status': 1})
        web_s.start()
        return web_s

    def event_handle(self):
        if not self.queue.empty():
            self.start_web_client()
        if not self.info_queue.empty():
            self.handle_info()
        pass

    def handle_info(self):
        task = self.info_queue.get()
        #{'name':self.username, 'data':data} 我拼的 data 为字串
        # index_str = task['data']
        username = task['name']
        # {'class':'command/info',  info:'' }
        '''
        发送信息测试: {"class": "info", "info": {"room": "世界", "message": "嘟嘟噜"}}
        {"class": "info", "info": {"room": "天字一号房", "message": "嘟嘟噜"}}
        设置名字:{"class": "command", "info": {"command": "set_nickname", "data": "小明"}}
        获取原初之名(进加密房的时候用的上): {"class": "command", "info": {"command": "get_username"}}
        创建房间:{"class": "command", "info": {"command":"create_room", "room":"天字二号房", "pw":"pw"}}
        {"class": "command", "info": {"command":"create_room", "room":"天字一号房", "pw":""}}  md5(原初名&&pw)
        进入房间:{"class": "command", "info": {"command":"join_room", "room":"天字一号房", "pw":""}}
        离开房间:{"class": "command", "info": {"command":"away_room", "room":"天字一号房"}}
        
        '''
        try:
            data = json.loads(task['data'])
            if data['class'] == 'info':  # {'room':'世界', 'message':''}
                tmp_room_name = data['info']['room']
                if username in self.room_map[tmp_room_name]['member']:
                    self.send_message_to_room(tmp_room_name,
                                              self.client_map[username]['thread'].nickname,
                                              data['info']['message'])
            elif data['class'] == 'command':
                self.handle_command(username, data['info'])
            else:
                self.send_message_all(json.dumps(task, ensure_ascii=False))
                pass
        except:
            self.send_message_all(json.dumps(task, ensure_ascii=False))
            pass

        # self.send_message_all(json.dumps(task, ensure_ascii=False))
        pass


    def handle_command(self, username, data):
        webscoket_c = self.client_map[username]['thread']
        #{'command':'set_nickname', data:'nickname' }
        command = data['command']
        # result = data
        data['class'] = 'command'
        if command == 'set_nickname': #{'command':'set_nickname', data:'nickname' }
            if data['data'] not in self.nickname_client:
                self.nickname_client[data['data']] = webscoket_c
                data['old_nickname'] = webscoket_c.nickname
                webscoket_c.nickname = data['data']
                data['status'] = 1
            else:
                data['status'] = 0
                data['error'] = '已被使用'
        elif command == 'get_username': #{'command':'get_username'}
            data['status'] = 1
            data['result'] = webscoket_c.username
        elif command == 'join_room': #{'command':'join_room' 'room':'room_name', 'pw':'name&&room_password of hash str'}
            room = self.room_map[data['room']]
            room_password = room['password']
            pw_true = ''
            if room_password != '':
                pw_true = self.get_md5('{}&&{}'.format(username, room_password).encode())
            if pw_true == data['pw']:
                data['status'] = 1
                room['member'].add(username)
            else:
                data['status'] = 0
                data['error'] = '密码错误'
        elif command == 'away_room': #{'command':'away_room' 'room':'room_name'}
            self.room_map[data['room']]['member'].remove(username)
            if len(self.room_map[data['room']]['member']) == 0:
                del self.room_map[data['room']]
            data['status'] = 1
        elif command == 'create_room':#{'command':'create_room' 'room':'room_name', 'pw':'pw'}
            if data['room'] in self.room_map:
                data['status'] = 0
                data['error'] = '房间名已存在'
            else:
                self.room_map[data['room']] = {'name': data['room'], 'password': data['pw'], 'member': set()}
                self.room_map[data['room']]['member'].add(username)
                data['status'] = 1
            pass
        webscoket_c.sendMessage(json.dumps(data, ensure_ascii=False))
        # 设定名字

        pass

    def work(self):
        self.start_web_server()
        while True:
            self.event_handle()

            time.sleep(1)

    def start_web_client(self):
        tmp_data = self.queue.get()
        c =WebsocketClient(tmp_data['socket'],tmp_data['name'], self)
        client_o = {
            'socket': tmp_data['socket'],
            'thread':c,
            'require':{}
        }
        self.client_map[tmp_data['name']] = client_o
        self.room_map['世界']['member'].add(tmp_data['name'])
        c.start()
        print(tmp_data)
        pass



class WebSocketServer(threading.Thread):
    def __init__(self,port, boss, num,):
        super(WebSocketServer,self).__init__()
        self.__port = port
        self.num = num
        self.__boss = boss
        pass

    def run(self):
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        sock.bind(('127.0.0.1', self.__port))
        sock.listen(5)
        # print('WebsocketServer start num:',self.num)
        while True:
            client_socket, address = sock.accept()
            print('新连接')
            try:
                username = str(address[0])+':'+str(address[1])
                self.__boss.queue.put({'name':username, 'socket': client_socket})
            except socket.timeout:
                print('websocket connection timeout!', address)


class WebsocketClient(threading.Thread):
    def __init__(self, client_socket, username, boss):
        super(WebsocketClient, self).__init__()
        self.client_socket = client_socket
        self.username = username
        m = hashlib.md5()
        m.update(username.encode())
        self.nickname = m.hexdigest()
        self.last_time = time.time()
        self.time_out_limit = 30*60
        self.__boss = boss
        self.__runining = True

    def stop_threading(self):
        self.sendMessage('断开连接')
        self.client_socket.shutdown(2)
        self.client_socket.close()
        self.__runining = False

    def run(self):
        data = self.client_socket.recv(1024)
        headers = self.parse_headers(data.decode('utf-8'))
        print(headers['Sec-WebSocket-Key'])
        token = self.generate_token(headers['Sec-WebSocket-Key'])
        print(token)
        token = str(token)[2:30] # 原始token含有 b' 真*toekn ' 要把b' ' 去掉
        print(token)
        sec_ws_ac = "Sec-WebSocket-Accept: "+ token + "\r\n"
        self.client_socket.send(bytes("HTTP/1.1 101 Web Socket Protocol Handshake\r\n", encoding="utf8"))
        self.client_socket.send(bytes("Upgrade: websocket\r\n", encoding="utf8"))
        self.client_socket.send(bytes(sec_ws_ac, encoding="utf8"))
        self.client_socket.send(bytes("Connection: Upgrade\r\n\r\n", encoding="utf8"))
        self.sendMessage('连接成功:{}'.format(self.username))
        while self.__runining:
            try:
                data = self.client_socket.recv(5000)
                print(data)
                if data == b'':
                    time.sleep(2)
                    if (time.time() - self.last_time) > self.time_out_limit:
                        self.sendMessage('好久没通信')
                        self.__boss.info_queue.put({'name': self.username, 'data': '断开连接'})
                    continue
            except socket.error as e:
                print("unexpected error: ", e)

            data = self.parse_data(data)
            data = data.decode('utf-8', errors='ignore')
            print("解析后:", data)
            if len(data) == 0:
                continue
            self.__boss.info_queue.put({'name': self.username, 'data':data})
            # message = self.username + ": " + data
                # notify(message)

    def parse_headers(self, msg):
        headers = {}
        header, data = msg.split('\r\n\r\n', 1)
        for line in header.split('\r\n')[1:]:
            key, value = line.split(': ', 1)
            headers[key] = value
        headers['data'] = data
        return headers
    def generate_token(self, msg):
        key = msg + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
        ser_key = hashlib.sha1(key.encode('utf-8')).digest()
        return base64.b64encode(ser_key)

    def t(self, msg):
        magic = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
        sha1 = hashlib.sha1()
        sha1.update((msg + magic).encode('utf-8'))
        return base64.b64encode(sha1.digest())

    def parse_data(self, data):
        # print(data)
        v = data[1] & 0x7f
        # 根据报文的第二个字节L确定报文长度所占的字节（1字节 = 8bit）数B
        #byte 里 的话应该是 1:2  1位1字
        # L < 126, B = 1
        # L == 126, B = 2
        # L == 127, B = 4
        if v == 0x7e:   # 126
            p = 4
        elif v == 0x7f: # 127 4
            p = 10 #为何不是6
        else:
            p = 2
        data_len = data[2: p]
        mask = data[p: p + 4]
        data = data[p + 4:]
        print(data)
        i = 0
        raw_str = ""
        ba = bytearray()
        for d in data:
            # raw_str += chr(d ^ mask[i % 4])
            ba.append(d ^ mask[i % 4])
            i += 1
        return ba

    def sendMessage(self, message):
        message = message.encode('utf-8')
        msgLen = len(message)
        backMsgList = []
        backMsgList.append(struct.pack('B', 129)) # 0x81 文本帧

        if msgLen <= 125:
            backMsgList.append(struct.pack('b', msgLen))
        elif msgLen <= 65535:
            backMsgList.append(struct.pack('b', 126))
            backMsgList.append(struct.pack('>h', msgLen))
        elif msgLen <= (2 ^ 64 - 1):
            backMsgList.append(struct.pack('b', 127))
            backMsgList.append(struct.pack('>h', msgLen))
        else:
            print("the message is too long to send in a time")
            return
        message_byte = bytes()
        print(type(backMsgList[0]))
        for c in backMsgList:
            # if type(c) != bytes:
            # print(bytes(c, encoding="utf8"))
            message_byte += c
        message_byte += message
        # print("message_str : ", str(message_byte))
        # print("message_byte : ", bytes(message_str, encoding="utf8"))
        # print(message_str[0], message_str[4:])
        # self.connection.send(bytes("0x810x010x63", encoding="utf8"))
        self.client_socket.send(message_byte)

if __name__ == '__main__':
    # obj = ReportWebsocket()
    # obj.work()

    obj = WebSocketManage()
    obj.work()
    # sN9cRrP / n9NdMgdcy2VJFQ==
    # fFBooB7FAkLlXgRSz0BT3v4hq5s =
    # wc = WebsocketClient(None,None,None)
    #
    # print(wc.t('sN9cRrP/n9NdMgdcy2VJFQ=='))
    # print(wc.generate_token('sN9cRrP/n9NdMgdcy2VJFQ=='))
    #
    # # dGhlIHNhbXBsZSBub25jZQ==
    # # s3pPLMBiTxaQ9kYGzzhZRbK + xOo =
    # print(wc.t('dGhlIHNhbXBsZSBub25jZQ=='))
    # print(wc.generate_token('dGhlIHNhbXBsZSBub25jZQ=='))


    # print(wc.t('jw1BfESKKj1oe5/L4IyAoQ=='))